﻿/* EntitiesToDTOs. Copyright (c) 2011. Fabian Fernandez.
 * http://entitiestodtos.codeplex.com
 * Licensed by Common Development and Distribution License (CDDL).
 * http://entitiestodtos.codeplex.com/license
 * Fabian Fernandez. 
 * http://www.linkedin.com/in/fabianfernandezb/en
 * */
using EnvDTE;
using EnvDTE80;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using EntitiesToDTOs.Properties;
using EntitiesToDTOs.Helpers;

namespace EntitiesToDTOs.Domain
{
    /// <summary>
    /// Represents a DTO to generate.
    /// </summary>
    internal class EntityDTO
    {
        /// <summary>
        /// The name found in the EDMX for this Entity Type.
        /// </summary>
        public string EntityName { get; private set; }

        /// <summary>
        /// DTO Class name.
        /// </summary>
        public string DTOName { get; private set; }

        /// <summary>
        /// DTO Base Class name.
        /// </summary>
        public string DTOBaseName { get; private set; }

        /// <summary>
        /// DTO Base Class reference.
        /// </summary>
        public EntityDTO DTOBase { get; private set; }

        /// <summary>
        /// DTOs childs of this DTO.
        /// </summary>
        public List<EntityDTO> DTOChilds { get; private set; }

        /// <summary>
        /// DTO Class access.
        /// </summary>
        public vsCMAccess ClassAccess { get; private set; }

        /// <summary>
        /// DTO Class Kind (used to generate partial classes).
        /// </summary>
        public vsCMClassKind ClassKind { get; private set; }

        /// <summary>
        /// Indicates if this DTO is Abstract.
        /// </summary>
        public bool IsAbstract { get; private set; }

        /// <summary>
        /// Class Attributes of this DTO.
        /// </summary>
        public List<EntityDTOAttribute> ClassAttributes { get; private set; }

        /// <summary>
        /// Properties of this DTO.
        /// </summary>
        public List<EntityDTOProperty> Properties { get; private set; }

        /// <summary>
        /// The Assembler to generate for this DTO.
        /// </summary>
        public Assembler Assembler { get; private set; }


        public EntityDTO(XElement entityNode, List<EntityNavigation> navigations, bool isServiceReady)
        {
            // Get the Entity name
            this.EntityName = entityNode.Attribute(EdmxNodeAttributes.EntityType_Name).Value;

            // Set the DTO name
            this.DTOName = EntityDTOHelper.GetDTOName(this.EntityName);

            // Define Class Base Type (if exists)
            this.DTOBaseName = EntityDTOHelper.GetEntityDTOBaseType(entityNode);

            // Set Class Access
            this.ClassAccess = vsCMAccess.vsCMAccessPublic;

            // Set the Class Kind to partial so the final user can extend it
            this.ClassKind = vsCMClassKind.vsCMClassKindPartialClass;

            #region Set IsAbstract
            if (entityNode.Attribute(EdmxNodeAttributes.EntityType_Abstract) != null)
            {
                string abstractValue = entityNode.Attribute(EdmxNodeAttributes.EntityType_Abstract).Value.ToLower();
                this.IsAbstract = (abstractValue == Resources.XmlBoolTrue);
            }
            #endregion Set IsAbstract

            #region Set Class Attributes
            this.ClassAttributes = new List<EntityDTOAttribute>();
            
            if (isServiceReady)
            {
                this.ClassAttributes.Add(new EntityDTOAttribute(Resources.AttributeDataContract, null));
            }
            #endregion Set Class Attributes

            #region Set Properties
            this.Properties = new List<EntityDTOProperty>();

            // Get Key Node
            XElement entityKeyNode = entityNode.Descendants(EdmxNodes.Key).FirstOrDefault();

            // Get the Entity Keys
            var entityKeys = new List<string>();
            if(entityKeyNode != null)
            {
                entityKeys = entityKeyNode.Descendants(EdmxNodes.PropertyRef)
                    .Select(n => n.Attribute(EdmxNodeAttributes.PropertyRef_Name).Value).ToList();
            }

            // Get Property Nodes
            IEnumerable<XElement> entityPropertiesNodes = entityNode.Descendants(EdmxNodes.Property);

            foreach (XElement propertyNode in entityPropertiesNodes)
            {
                this.Properties.Add(new EntityDTOProperty(propertyNode, entityKeys, this.EntityName, isServiceReady));
            }
            #endregion Set Properties

            #region Set Navigation Properties
            if (navigations != null)
            {
                IEnumerable<EntityNavigation> myNavigations = navigations.Where(a => a.DTOName == this.DTOName);

                foreach (EntityNavigation entityNav in myNavigations)
                {
                    foreach (EntityNavigationProperty navProperty in entityNav.NavigationProperties)
                    {
                        this.Properties.Add(new EntityDTOProperty(
                            entityNav.NavigationPropertyNameEDMX, navProperty, isServiceReady));
                    }
                }
            }
            #endregion Set Association Properties

            // Set Assembler (it does not have one by default)
            this.Assembler = null;
        }


        /// <summary>
        /// Sets the KnowTypes of this DTO.
        /// </summary>
        /// <param name="entities">List of all the DTOs that are going to be generated.</param>
        /// <param name="isServiceReady">Specifies if the DTOs are Service-Ready.</param>
        public void SetKnownTypes(List<EntityDTO> entities, bool isServiceReady)
        {
            if (isServiceReady)
            {
                IEnumerable<EntityDTO> knownTypes = entities.Where(e => e.DTOBaseName == this.DTOName);

                foreach (EntityDTO entity in knownTypes)
                {
                    this.ClassAttributes.Add(new EntityDTOAttribute(Resources.AttributeKnownType,
                            string.Format(Resources.CSharpCodeTypeOf, entity.DTOName)));
                }
            }
        }

        /// <summary>
        /// Sets the childs of this DTO.
        /// </summary>
        /// <param name="entities">List of all the DTOs that are going to be generated.</param>
        public void SetChilds(List<EntityDTO> entities)
        {
            this.DTOChilds = entities.Where(e => e.DTOBaseName == this.DTOName).ToList();
        }

        /// <summary>
        /// Sets the reference to the DTO Base Class.
        /// </summary>
        /// <param name="entitiesDTOs">Entities DTOs to generate.</param>
        public void SetDTOBase(List<EntityDTO> entitiesDTOs)
        {
            if (string.IsNullOrWhiteSpace(this.DTOBaseName) == false)
            {
                EntityDTO baseDTO = entitiesDTOs.FirstOrDefault(e => e.DTOName == this.DTOBaseName);

                this.DTOBase = baseDTO;
            }
        }

        /// <summary>
        /// Sets the Assembler for this DTO.
        /// </summary>
        public void SetAssembler()
        {
            this.Assembler = new Assembler(this);
        }
    }
}